//	--------------------------------------------------------------------
//	Copyright(C) 2006,2007 Zhao Yukun. All rights reserved.
//	This file is a part of the Xeres system.
//	Xeres 2007
//	Contact : xeres.engine@gmail.com
//	$(reservedInfo)
//	$(reservedDeclaration)
//	--------------------------------------------------------------------
///	@file	<half.inl>
///	@path	~/src/lib/math/
///	@date	2007/09/29
///	@desc	half -- a 16-bit floating point number class, from ILMBase.

#pragma once

#include "lib/math/half.h"

namespace xeres {

	// ctor
	inline half::half( void )
	{
	}

	inline half half::to_half( const float f )
	{
		ushort hh;

		uif x;
		x.f = f;

		if (f == 0)
		{
			// Common special case - zero.
			// Preserve the zero's sign bit.
			hh = (x.i >> 16);
		}
		else
		{
			// We extract the combined sign and exponent, e, from our
			// floating-point number, f.  Then we convert e to the sign
			// and exponent of the half number via a table lookup.
			//
			// For the most common case, where a normalized half is produced,
			// the table lookup returns a non-zero value; in this case, all
			// we have to do is round f's significand to 10 bits and combine
			// the result with e.
			//
			// For all other cases (overflow, zeroes, denormalized numbers
			// resulting from underflow, infinities and NANs), the table
			// lookup returns zero, and we call a longer, non-inline function
			// to do the float-to-half conversion.
			register int e = (x.i >> 23) & 0x000001ff;
			e = s_eLut[e];
			if (e)
			{
				// Simple case - round the significand, m, to 10
				// bits and combine it with the sign and exponent.
				register int m = x.i & 0x007fffff;
				hh = e + ((m + 0x00000fff + ((m >> 13) & 1)) >> 13);
			}
			else
			{
				// Difficult case - call a function.
				hh = convert (x.i);
			}
		}

		half h;
		h.m_h = hh;
		return h;
	}

	inline half::half( const float f )
	{
		uif x;
		x.f = f;

		if (f == 0)
		{
			// Common special case - zero.
			// Preserve the zero's sign bit.
			m_h = (x.i >> 16);
		}
		else
		{
			// We extract the combined sign and exponent, e, from our
			// floating-point number, f.  Then we convert e to the sign
			// and exponent of the half number via a table lookup.
			//
			// For the most common case, where a normalized half is produced,
			// the table lookup returns a non-zero value; in this case, all
			// we have to do is round f's significand to 10 bits and combine
			// the result with e.
			//
			// For all other cases (overflow, zeroes, denormalized numbers
			// resulting from underflow, infinities and NANs), the table
			// lookup returns zero, and we call a longer, non-inline function
			// to do the float-to-half conversion.
			register int e = (x.i >> 23) & 0x000001ff;
			e = s_eLut[e];
			if (e)
			{
				// Simple case - round the significand, m, to 10
				// bits and combine it with the sign and exponent.
				register int m = x.i & 0x007fffff;
				m_h = e + ((m + 0x00000fff + ((m >> 13) & 1)) >> 13);
			}
			else
			{
				// Difficult case - call a function.
				m_h = convert (x.i);
			}
		}
	}

	// copier
	inline half::half( const half& rhs )
		: m_h( rhs.m_h )
	{
	}

	// positive infinite
	inline half half::positive_infinite( void )
	{
		half h;
		h.m_h = 0x7c00;
		return h;
	}

	// negative infinite
	inline half half::negative_infinite( void )
	{
		half h;
		h.m_h = 0xfc00;
		return h;
	}

	inline half half::q_nan( void )
	{
		half h;
		h.m_h = 0x7fff;
		return h;
	}

	inline half half::s_nan( void )
	{
		half h;
		h.m_h = 0x7dff;
		return h;
	}

	//-------------------------
	// Round to n-bit precision
	//-------------------------

	inline half half::round( const uint n ) const
	{
		// Parameter check.
		if (n >= 10)
			return *this;

		// Disassemble h into the sign, s,
		// and the combined exponent and significand, e.
		unsigned short s = m_h & 0x8000;
		unsigned short e = m_h & 0x7fff;

		// Round the exponent and significand to the nearest value
		// where ones occur only in the (10-n) most significant bits.
		// Note that the exponent adjusts automatically if rounding
		// up causes the significand to overflow.

		e >>= 9 - n;
		e  += e & 1;
		e <<= 9 - n;

		// Check for exponent overflow.

		if (e >= 0x7c00)
		{
			// Overflow occurred -- truncate instead of rounding.
			e = m_h;
			e >>= 10 - n;
			e <<= 10 - n;
		}

		// Put the original sign bit back.
		half h;
		h.m_h = s | e;
		return h;
	}

	inline bool	half::is_finite( void ) const
	{
		unsigned short e = (m_h >> 10) & 0x001f;
		return e < 31;
	}

	inline bool half::is_infinite( void ) const
	{
		unsigned short e = (m_h >> 10) & 0x001f;
		unsigned short m =  m_h & 0x3ff;
		return e == 31 && m == 0;
	}

	inline bool half::is_normalized( void ) const
	{
		unsigned short e = (m_h >> 10) & 0x001f;
		return e > 0 && e < 31;
	}

	inline bool half::is_denormalized( void ) const
	{
		unsigned short e = (m_h >> 10) & 0x001f;
		unsigned short m =  m_h & 0x3ff;
		return e == 0 && m != 0;
	}

	inline bool half::is_zero( void ) const
	{
		return (m_h & 0x7fff) == 0;
	}

	inline bool	half::is_negative ( void ) const
	{
		return (m_h & 0x8000) != 0;
	}

	inline bool half::is_nan( void ) const
	{
		unsigned short e = (m_h >> 10) & 0x001f;
		unsigned short m =  m_h & 0x3ff;
		return e == 31 && m != 0;
	}

	inline unsigned short half::get_bits ( void ) const
	{
		return m_h;
	}

	inline void half::set_bits( ushort bits )
	{
		m_h = bits;
	}

	//------------------------------------------
	// Half-to-float conversion via table lookup
	//------------------------------------------

	inline half::operator float( void ) const
	{
		return s_toFloat[m_h].f;
	}

	inline half	half::operator - ( void ) const
	{
		half h;
		h.m_h = m_h ^ 0x8000;
		return h;
	}


	inline half & half::operator = ( half h )
	{
		m_h = h.m_h;
		return *this;
	}


	inline half & half::operator = ( float f )
	{
		uif x;
		x.f = f;

		if (f == 0)
		{
			// Common special case - zero.
			// Preserve the zero's sign bit.
			m_h = (x.i >> 16);
		}
		else
		{
			// We extract the combined sign and exponent, e, from our
			// floating-point number, f.  Then we convert e to the sign
			// and exponent of the half number via a table lookup.
			//
			// For the most common case, where a normalized half is produced,
			// the table lookup returns a non-zero value; in this case, all
			// we have to do is round f's significand to 10 bits and combine
			// the result with e.
			//
			// For all other cases (overflow, zeroes, denormalized numbers
			// resulting from underflow, infinities and NANs), the table
			// lookup returns zero, and we call a longer, non-inline function
			// to do the float-to-half conversion.
			register int e = (x.i >> 23) & 0x000001ff;
			e = s_eLut[e];
			if (e)
			{
				// Simple case - round the significand, m, to 10
				// bits and combine it with the sign and exponent.
				register int m = x.i & 0x007fffff;
				m_h = e + ((m + 0x00000fff + ((m >> 13) & 1)) >> 13);
			}
			else
			{
				// Difficult case - call a function.
				m_h = convert (x.i);
			}
		}
	}


	inline half & half::operator += ( half h )
	{
		*this = to_half( float(*this) + float(h) );
		return *this;
	}


	inline half & half::operator += ( float f )
	{
		*this = to_half( float(*this) + f );
		return *this;
	}


	inline half & half::operator -= ( half h )
	{
		*this = to_half( float(*this) - float(h) );
		return *this;
	}


	inline half & half::operator -= ( float f )
	{
		*this = to_half( float(*this) - f );
		return *this;
	}


	inline half & half::operator *= ( half h )
	{
		*this = to_half( float(*this) * float(h) );
		return *this;
	}

	inline half & half::operator *= ( float f )
	{
		*this = to_half( float(*this) * f );
		return *this;
	}


	inline half & half::operator /= ( half h )
	{
		*this = to_half( float(*this) / float(h) );
		return *this;
	}


	inline half & half::operator /= ( float f )
	{
		*this = to_half( float(*this) / f );
		return *this;
	}

} // namespace xeres
